package gee

import (
	"html/template"
	"log"
	"net/http"
	"path"
	"strings"
)

// change: support static template

// HandleFunc 函数作为值、类型
type HandleFunc func(ctx *Context)

type RouterGroup struct {
	prefix      string       //
	middlewares []HandleFunc // support middlewares
	engine      *Engine      // all groups share a Engine instance
}

// Engine is the nil handle for all requests
// 所有请求的句柄
type Engine struct {
	// 路由映射表
	router *router
	*RouterGroup
	groups       []*RouterGroup     // store all groups
	htmlTemplates *template.Template // for html render
	funcMap      template.FuncMap   // for html render

}

func New() *Engine {
	engine := &Engine{router: newRouter()}
	engine.RouterGroup = &RouterGroup{engine: engine}
	engine.groups = []*RouterGroup{engine.RouterGroup}
	return engine
}

// Router Group method

func (group *RouterGroup) Group(prefix string) *RouterGroup {
	engine := group.engine
	newGroup := &RouterGroup{
		prefix: engine.prefix + prefix,
		// middlewares: nil,
		engine: engine,
	}
	engine.groups = append(engine.groups, newGroup)
	return newGroup
}

// Router method

// Use is defined to add middleware to the group
func (group *RouterGroup) Use(middlewares ...HandleFunc) {
	group.middlewares = append(group.middlewares, middlewares...)
}

// Router method
func (group *RouterGroup) addRouter(method string, comp string, handle HandleFunc) {
	pattern := group.prefix + comp
	log.Printf("Router %4s - %s", method, pattern)
	group.engine.router.addRoute(method, pattern, handle)
}

// GET defined the method to add Get request
func (group *RouterGroup) GET(pattern string, handle HandleFunc) {
	group.addRouter("GET", pattern, handle)
}

// POST defined the method to add Post request
func (group *RouterGroup) POST(pattern string, handle HandleFunc) {
	group.addRouter("POST", pattern, handle)
}

// static handle
func (group *RouterGroup) createStaticHandle(relativePath string, fs http.FileSystem) HandleFunc {
	absolutePath := path.Join(group.prefix, relativePath)
	fileServer := http.StripPrefix(absolutePath, http.FileServer(fs))
	return func(c *Context) {
		file := c.Param("filepath")
		// 检查文件是否存在以及权限操作
		_, err := fs.Open(file)
		if err != nil {
			c.Status(http.StatusNotFound)
			return
		}
		fileServer.ServeHTTP(c.Write, c.Req)
	}
}

// 将root文件夹映射到路由relativePath
// eg r.Static("/asserts","/usr/geektutu/blob/static")
// or r.Static("/asserts","./static")
func (group *RouterGroup) Static(relativePath string, root string) {
	handle := group.createStaticHandle(relativePath, http.Dir(root))
	urlPattern := path.Join(relativePath, "/*filepath")

	group.GET(urlPattern, handle)
}

// ///////////////////////////////////////////////////////////////////////////
// Router method

func (engine *Engine) addRouter(method string, pattern string, handle HandleFunc) {
	engine.router.addRoute(method, pattern, handle)
}

// GET defined the method to add Get request
func (engine *Engine) GET(pattern string, handle HandleFunc) {
	engine.addRouter("GET", pattern, handle)
}

// POST defined the method to add Post request
func (engine *Engine) POST(pattern string, handle HandleFunc) {
	engine.addRouter("POST", pattern, handle)
}

func (engine *Engine) SetFuncMap(funcMap template.FuncMap) {
	engine.funcMap = funcMap
}

// LoadHtmlGlob 加载模板
func (engine *Engine) LoadHtmlGlob(pattern string) {
	engine.htmlTemplates = template.Must(template.New("").Funcs(engine.funcMap).ParseGlob(pattern))
}

// Run attaches to a http.Server and starts listening and serving HTTP requests.
// It is a shortcut for http.ListenAndServe(addr, router)
// Note: this method will block the calling goroutine indefinitely unless an error happens.
func (engine *Engine) Run(addr string) (err error) {
	return http.ListenAndServe(addr, engine)
}

// 解析请求的路径，查找路由映射表
func (engine *Engine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// 判断具体的请求适用哪些中间件，通过URL的前缀来判断
	var middlewares []HandleFunc
	for _, group := range engine.groups {
		if strings.HasPrefix(r.URL.Path, group.prefix) {
			middlewares = append(middlewares, group.middlewares...)
		}
	}

	c := newContext(w, r)
	c.handlers = middlewares
	c.engine = engine
	engine.router.handle(c)
}
